Skip to content

fix: use context variables for block outputs in function block code#4223

Open
octo-patch wants to merge 30 commits intosimstudioai:stagingfrom
octo-patch:fix/issue-4195-function-block-context-vars
Open

fix: use context variables for block outputs in function block code#4223
octo-patch wants to merge 30 commits intosimstudioai:stagingfrom
octo-patch:fix/issue-4195-function-block-context-vars

Conversation

@octo-patch
Copy link
Copy Markdown

Problem

When a function block references another block's output via <BlockA.result>, the executor embeds the full value as a JavaScript literal directly in the code string. For large outputs this causes two issues:

  1. The code string exceeds the terminal console's 50,000-character display limit, so users see truncated code in the block's input panel.
  2. When the serialized inputs exceed 256 KB the terminal replaces the entire input with { __simTruncated: true }, making it look like the function block received no data.

The actual execution was correct, but the misleading display caused users to believe their function block was broken. Closes #4195.

Solution

Block output references in function block code are now resolved to named context variables (__blockRef_N) instead of inlined JavaScript literals. The resolved values are passed to the isolated VM as global variables, exactly like workflow variables and env vars already are.

Execution path after this fix:

  1. resolveCodeWithContextVars() replaces <BlockA.result> with __blockRef_0 in the code string and stores the actual value in contextVariables.__blockRef_0.
  2. The function handler passes these contextVariables to the API route.
  3. The API route merges them with its own context variables and passes everything to executeInIsolatedVM.
  4. The isolated VM worker sets global.__blockRef_0 = <value> before running user code, so the reference resolves correctly.

What is NOT changed:

  • Loop/parallel iteration variables (e.g. <loop.currentItem>) are still inlined as literals — the API route has no visibility into runtime loop state.
  • Execution results are unchanged; only the code string representation is smaller.
  • blockData is still passed for backward compatibility and custom tool use cases.

Files Changed

File Change
executor/variables/resolver.ts New resolveInputsForFunctionBlock() method + resolveCodeWithContextVars() helper
executor/execution/block-executor.ts Use new resolution path for function blocks; filter internal key from logs
executor/handlers/function/function-handler.ts Extract context vars from inputs and pass to executeTool
tools/function/types.ts Add contextVariables field to CodeExecutionInput
tools/function/execute.ts Include contextVariables in the request body
app/api/function/execute/route.ts Accept and merge pre-resolved context variables

waleedlatif1 and others added 29 commits April 3, 2026 23:30
…ership workflow edits via sockets, ui improvements
…ration, signup method feature flags, SSO improvements
* feat(posthog): Add tracking on mothership abort (simstudioai#4023)

Co-authored-by: Theodore Li <theo@sim.ai>

* fix(login): fix captcha headers for manual login  (simstudioai#4025)

* fix(signup): fix turnstile key loading

* fix(login): fix captcha header passing

* Catch user already exists, remove login form captcha
…nts, secrets performance, polling refactors, drag resources in mothership
…endar triggers, docs updates, integrations/models pages improvements
…mat, logs performance improvements

fix(csp): add missing analytics domains, remove unsafe-eval, fix workspace CSP gap (simstudioai#4179)
fix(landing): return 404 for invalid dynamic route slugs (simstudioai#4182)
improvement(seo): optimize sitemaps, robots.txt, and core web vitals across sim and docs (simstudioai#4170)
fix(gemini): support structured output with tools on Gemini 3 models (simstudioai#4184)
feat(brightdata): add Bright Data integration with 8 tools (simstudioai#4183)
fix(mothership): fix superagent credentials (simstudioai#4185)
fix(logs): close sidebar when selected log disappears from filtered list; cleanup (simstudioai#4186)
v0.6.46: mothership streaming fixes, brightdata integration
When a function block references another block's output via <BlockA.result>,
the executor previously embedded the full value as a JavaScript literal
directly in the code string. For large outputs (>50 KB), this caused the code
string to exceed the terminal console display limit, making inputs appear
truncated or replaced with { __simTruncated: true } in the UI.

Instead, block output references in function block code are now stored as
named global variables (__blockRef_N) in the isolated VM context. The code
string only contains the compact variable name, keeping it small regardless
of the referenced value size.

Loop/parallel/env/workflow references are still inlined as literals since
the API route has no way to resolve them independently.

The _runtimeContextVars key is filtered from sanitizeInputsForLog so it
does not appear in execution logs or SSE events.

Pre-resolved context variables are merged with any variables produced by
the API route resolveCodeVariables, with executor values taking precedence.

Fixes simstudioai#4195
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 18, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Apr 18, 2026 3:11am

Request Review

@cursor
Copy link
Copy Markdown

cursor bot commented Apr 18, 2026

PR Summary

Medium Risk
Changes how function-block code templates resolve block output references and how those values are passed into the execution VM, which could affect runtime behavior if variable precedence or serialization differs from the prior inline-literal approach.

Overview
Function block code no longer inlines referenced block outputs as large literals; those references are replaced with stable __blockRef_N identifiers and the actual values are accumulated into a contextVariables map during input resolution.

The executor now carries these pre-resolved context vars through block execution (hidden from logs) and the function execution tool/API route accept and merge them (including for shell), injecting them as VM globals/envs at runtime to avoid oversized serialized inputs while preserving execution semantics.

Reviewed by Cursor Bugbot for commit 2783606. Bugbot is set up for automated code reviews on this repo. Configure here.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 18, 2026

Greptile Summary

This PR fixes a display bug where embedding large block outputs as JavaScript literals in function block code caused the inputs panel to appear truncated or replaced with { __simTruncated: true }. Block output references (<BlockA.result>) are now resolved to named context variables (__blockRef_N) that are passed to the isolated VM as globals, keeping the serialized code string small while leaving execution results unchanged.

Confidence Score: 5/5

Safe to merge — the fix is logically sound, execution results are unchanged, and the only finding is a duplicate dead-code interface declaration.

All findings are P2 (style/cleanup). The core logic — replacing block output references with named context variables, passing them through the tool/route pipeline, and injecting them as VM globals — is correct and non-overlapping with the route's own variable naming schemes.

apps/sim/tools/function/types.ts — accidental duplicate CodeExecutionOutput interface should be removed before merging to keep the type file clean.

Important Files Changed

Filename Overview
apps/sim/tools/function/types.ts Adds contextVariables field to CodeExecutionInput correctly, but accidentally duplicates the existing CodeExecutionOutput interface verbatim — the extra declaration is dead code and should be removed.
apps/sim/executor/variables/resolver.ts Adds resolveInputsForFunctionBlock and resolveCodeWithContextVars; logic correctly distinguishes block vs. non-block references via SPECIAL_REFERENCE_PREFIXES and stores block output values as __blockRef_N globals instead of inlining them.
apps/sim/executor/execution/block-executor.ts Correctly branches on BlockType.FUNCTION to use the new resolution path and filters FUNCTION_BLOCK_CONTEXT_VARS_KEY from sanitized log inputs.
apps/sim/executor/handlers/function/function-handler.ts Extracts pre-resolved context variables from inputs and forwards them cleanly to executeTool.
apps/sim/app/api/function/execute/route.ts Accepts preResolvedContextVariables from the request body and merges them with the route's own resolution, with executor-side vars taking correct precedence; naming schemes (__blockRef_N vs __variable_X / __tag_X) are non-overlapping.
apps/sim/tools/function/execute.ts Correctly passes contextVariables through to the API request body with a safe default of {}.

Sequence Diagram

sequenceDiagram
    participant Executor as BlockExecutor
    participant Resolver as VariableResolver
    participant Handler as FunctionBlockHandler
    participant Tool as functionExecuteTool
    participant Route as /api/function/execute
    participant VM as IsolatedVM

    Executor->>Resolver: resolveInputsForFunctionBlock(params)
    Resolver->>Resolver: resolveCodeWithContextVars(code)
    Note over Resolver: replaces BlockA.result ref with __blockRef_0 and stores value
    Resolver-->>Executor: resolvedInputs plus contextVariables
    Executor->>Handler: execute(inputs)
    Handler->>Handler: extract inputs[_runtimeContextVars]
    Handler->>Tool: executeTool with code and contextVariables
    Tool->>Route: POST with code, contextVariables, blockData
    Route->>Route: resolveCodeVariables(code)
    Route->>Route: merge codeResolution.contextVariables with preResolvedContextVariables
    Route->>VM: executeInIsolatedVM(code, contextVariables)
    VM->>VM: global.__blockRef_0 = value
    VM-->>Route: result
Loading

Reviews (1): Last reviewed commit: "fix: use context variables for block out..." | Re-trigger Greptile

Comment thread apps/sim/tools/function/types.ts Outdated
Comment on lines +38 to +43
export interface CodeExecutionOutput extends ToolResponse {
output: {
result: any
stdout: string
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Duplicate interface declaration

CodeExecutionOutput is already declared at line 31–36 with an identical shape. TypeScript's declaration merging allows this without a compile error, but the second block is dead code accidentally introduced by this PR. Remove it.

Suggested change
export interface CodeExecutionOutput extends ToolResponse {
output: {
result: any
stdout: string
}
}

Comment thread apps/sim/executor/variables/resolver.ts Outdated
Comment on lines +249 to +254
if (this.blockResolver.canResolve(match)) {
// Block output: store in contextVarAccumulator, replace with variable name
const varName = `__blockRef_${Object.keys(contextVarAccumulator).length}`
contextVarAccumulator[varName] = effectiveValue
return varName
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Repeated context variable allocation for duplicate block references

When the same block reference (e.g. <BlockA.result>) appears more than once in the code template, each occurrence resolves independently and is stored under a new key (__blockRef_0, __blockRef_1, …), duplicating the payload value. Consider deduplicating by keying on the reference string before allocating a new slot. This is not a correctness issue — just a small efficiency improvement worth considering.

Comment thread apps/sim/app/api/function/execute/route.ts
Comment thread apps/sim/tools/function/types.ts Outdated
@icecrasher321 icecrasher321 changed the base branch from main to staging April 18, 2026 03:48
@gitguardian
Copy link
Copy Markdown

gitguardian bot commented Apr 18, 2026

⚠️ GitGuardian has uncovered 1 secret following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

Since your pull request originates from a forked repository, GitGuardian is not able to associate the secrets uncovered with secret incidents on your GitGuardian dashboard.
Skipping this check run and merging your pull request will create secret incidents on your GitGuardian dashboard.

🔎 Detected hardcoded secret in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
29606901 Triggered Generic High Entropy Secret a54dcbe apps/sim/providers/utils.test.ts View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secret safely. Learn here the best practices.
  3. Revoke and rotate this secret.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@icecrasher321
Copy link
Copy Markdown
Collaborator

@octo-patch please address the automated PR comments and then I can review this. If they are not relevant, please leave a comment there and mark them as resolved.

@icecrasher321 icecrasher321 self-assigned this Apr 18, 2026
- Pass preResolvedContextVariables through to shellEnvs for Shell language
  (Cursor: shell loses pre-resolved block refs, executes against undefined vars)
- Remove duplicate CodeExecutionOutput interface declaration
  (Cursor + Greptile: dead duplicate declaration in tools/function/types.ts)
- Deduplicate identical block references in resolveCodeWithContextVars so the
  same <BlockA.result> reused multiple times shares one __blockRef_N slot
  (Greptile P2: avoid duplicating large payloads across the wire)
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 18, 2026

Someone is attempting to deploy a commit to the Sim Team on Vercel.

A member of the Team first needs to authorize it.

@octo-patch
Copy link
Copy Markdown
Author

@icecrasher321 Thanks for the review. I have pushed a follow-up commit addressing the bot comments:

  • Cursor (medium): Shell language loses pre-resolved context variables. Fixed. The Shell branch in apps/sim/app/api/function/execute/route.ts now seeds contextVariables from preResolvedContextVariables so the executor-supplied __blockRef_N values are exposed as shell env vars and referenced block outputs are not undefined at runtime.

  • Cursor (low) + Greptile (P2): Duplicate CodeExecutionOutput interface in apps/sim/tools/function/types.ts. Fixed. Removed the duplicate declaration.

  • Greptile (P2): Repeated context variable allocation for duplicate block references. Addressed. resolveCodeWithContextVars now deduplicates by the matched reference string so identical <BlockA.result> occurrences share a single __blockRef_N slot and the payload value is not duplicated across the wire.

  • GitGuardian: Generic high-entropy secret in apps/sim/providers/utils.test.ts. Not relevant to this PR. The flagged commit a54dcbe is not part of this PR; the diff for this branch does not touch apps/sim/providers/utils.test.ts. The match looks like a pre-existing test fixture.

Existing tests still pass: app/api/function/execute/route.test.ts (30/30) and executor/variables/ suite (196/196).

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2783606. Configure here.

const varName = `__blockRef_${Object.keys(contextVarAccumulator).length}`
contextVarAccumulator[varName] = effectiveValue
blockRefByMatch.set(match, varName)
return varName
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shell block references break: missing $ prefix and wrong serialization

Medium Severity

For shell function blocks, resolveCodeWithContextVars replaces <BlockA.result> with bare __blockRef_0, but shell requires $__blockRef_0 to dereference an environment variable. The code runs echo __blockRef_0 and outputs the literal string instead of the value. Additionally, in the route, String(v) on complex objects stored as shell env vars produces [object Object] rather than a JSON representation. Before this PR, values were inlined via formatLiteralForCode, so this is a regression for shell function blocks referencing other block outputs.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2783606. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Function block truncating input and returning bad result

5 participants